/*
 * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package com.xuexiang.xui_lib.component.imageview.crop;

import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.Image;
import ohos.agp.components.StackLayout;
import ohos.agp.utils.Matrix;
import ohos.agp.utils.Rect;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.global.resource.NotExistException;
import ohos.global.resource.RawFileEntry;
import ohos.global.resource.Resource;
import ohos.global.resource.WrongTypeException;
import ohos.media.image.ImagePacker;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;
import ohos.media.image.common.ImageInfo;
import ohos.media.image.common.PropertyKey;
import ohos.media.image.common.Size;

import com.xuexiang.xui_lib.util.AttrValue;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileDescriptor;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Optional;

/**
 * 自定义图片裁剪控件
 *
 * @author xuexiang
 * @since 2019-10-15 11:53
 */
public class CropImageView extends StackLayout {
    // Private Constants ///////////////////////////////////////////////////////

    private static final Rect EMPTY_RECT = new Rect();

    // Member Variables ////////////////////////////////////////////////////////

    // Sets the default image guidelines to show when resizing
    public static final int DEFAULT_GUIDELINES = 1;
    public static final boolean DEFAULT_FIXED_ASPECT_RATIO = false;
    public static final int DEFAULT_ASPECT_RATIO_X = 1;
    public static final int DEFAULT_ASPECT_RATIO_Y = 1;

    private static final int DEFAULT_IMAGE_RESOURCE = 0;

    private static final String DEGREES_ROTATED = "DEGREES_ROTATED";

    private Image mImageView;
    private CropOverlayView mCropOverlayView;

    private PixelMap mBitmap;
    private int mDegreesRotated = 0;

    private int mLayoutWidth;
    private int mLayoutHeight;
    private ImageSource cacheImageSource;
    private int cacheRotate = 0;
    // Instance variables for customizable attributes
    private int mGuidelines = DEFAULT_GUIDELINES;
    private boolean mFixAspectRatio = DEFAULT_FIXED_ASPECT_RATIO;
    private int mAspectRatioX = DEFAULT_ASPECT_RATIO_X;
    private int mAspectRatioY = DEFAULT_ASPECT_RATIO_Y;
    private int mImageResource = DEFAULT_IMAGE_RESOURCE;
   /* private int oldWidth;
    private int oldHeight;*/
    // Constructors ////////////////////////////////////////////////////////////

    private static final String CropImageView_civ_guidelines = "civ_guidelines";
    //    <!-- 是否是固定比例裁剪 -->
    private static final String CropImageView_civ_fixAspectRatio = "civ_fixAspectRatio";
    //    <!-- 宽度比例 -->
    private static final String CropImageView_civ_aspectRatioX = "civ_aspectRatioX";
    //      <!-- 高度比例 -->
    private static final String CropImageView_civ_aspectRatioY = "civ_aspectRatioY";
    //     <!-- 裁剪图片的资源 -->
    private static final String CropImageView_civ_imageResource = "civ_imageResource";

    public CropImageView(Context context) {
        this(context, null);
    }

    public CropImageView(Context context, AttrSet attrSet) {
        this(context, attrSet, "");
    }

    public CropImageView(Context context, AttrSet attrs, String styleName) {
        super(context, attrs, styleName);
        mGuidelines = AttrValue.get(attrs, CropImageView_civ_guidelines, DEFAULT_GUIDELINES);
        mFixAspectRatio = AttrValue.get(attrs, CropImageView_civ_fixAspectRatio, DEFAULT_FIXED_ASPECT_RATIO);
        mAspectRatioX = AttrValue.get(attrs, CropImageView_civ_aspectRatioX, DEFAULT_ASPECT_RATIO_X);
        mAspectRatioY = AttrValue.get(attrs, CropImageView_civ_aspectRatioY, DEFAULT_ASPECT_RATIO_Y);
        mImageResource = AttrValue.get(attrs, CropImageView_civ_imageResource, DEFAULT_IMAGE_RESOURCE);
        init(context);
    }

    // Private Methods /////////////////////////////////////////////////////////
    private void init(Context context) {
        mImageView = new Image(context);
        mImageView.setLayoutConfig(getLayoutConfig());
        mImageView.setScaleMode(Image.ScaleMode.INSIDE);
        addComponent(mImageView);
        mCropOverlayView = new CropOverlayView(context);
        mCropOverlayView.setLayoutConfig(getLayoutConfig());
        addComponent(mCropOverlayView);

        mCropOverlayView.setInitialAttributeValues(mGuidelines, mFixAspectRatio, mAspectRatioX, mAspectRatioY);

        setEstimateSizeListener(new EstimateSizeListener() {
            @Override
            public boolean onEstimateSize(int widthMeasureSpec, int heightMeasureSpec) {
                onMeasure(widthMeasureSpec, heightMeasureSpec);
                return false;
            }
        });
        setLayoutRefreshedListener(new LayoutRefreshedListener() {
            @Override
            public void onRefreshed(Component component) {
                //  onLayout();
                onSizeChanged(component);
            }
        });
        onLayout();
    }

    private void onSizeChanged(Component component) {
        if (mBitmap != null) {
            final Rect bitmapRect = ImageViewUtil.getBitmapRectCenterInside(mBitmap, this);
            mCropOverlayView.setBitmapRect(bitmapRect);
        } else {
            mCropOverlayView.setBitmapRect(EMPTY_RECT);
        }
    }

    private boolean onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = EstimateSpec.getMode(widthMeasureSpec);
        int widthSize = EstimateSpec.getSize(widthMeasureSpec);
        int heightMode = EstimateSpec.getMode(heightMeasureSpec);
        int heightSize = EstimateSpec.getSize(heightMeasureSpec);
        //1073741824==1080
        // showLog(widthMode+"=="+widthSize);
        if (mBitmap != null) {
            Size mBitmapSize = mBitmap.getImageInfo().size;
            // Bypasses a baffling bug when used within a ScrollView, where
            // heightSize is set to 0.
            if (heightSize == 0) {
                heightSize = mBitmapSize.height;
            }

            int desiredWidth;
            int desiredHeight;

            double viewToBitmapWidthRatio = Double.POSITIVE_INFINITY;
            double viewToBitmapHeightRatio = Double.POSITIVE_INFINITY;

            // Checks if either width or height needs to be fixed
            if (widthSize < mBitmapSize.width) {
                viewToBitmapWidthRatio = (double) widthSize / (double) mBitmapSize.width;
            }
            if (heightSize < mBitmapSize.height) {
                viewToBitmapHeightRatio = (double) heightSize / (double) mBitmapSize.height;
            }

            // If either needs to be fixed, choose smallest ratio and calculate
            // from there
            /*if (viewToBitmapWidthRatio != Double.POSITIVE_INFINITY || viewToBitmapHeightRatio != Double.POSITIVE_INFINITY) {
                if (viewToBitmapWidthRatio <= viewToBitmapHeightRatio) {
                    desiredWidth = widthSize;
                    desiredHeight = (int) (mBitmapSize.height * viewToBitmapWidthRatio);
                } else {
                    desiredHeight = heightSize;
                    desiredWidth = (int) (mBitmapSize.width * viewToBitmapHeightRatio);
                }
            }*/
            if (BigDecimal.valueOf(viewToBitmapWidthRatio).compareTo(BigDecimal.valueOf(Double.POSITIVE_INFINITY)) != 0
                || BigDecimal.valueOf(viewToBitmapHeightRatio).compareTo(BigDecimal.valueOf(Double.POSITIVE_INFINITY)) != 0) {
                if (viewToBitmapWidthRatio <= viewToBitmapHeightRatio) {
                    desiredWidth = widthSize;
                    desiredHeight = (int) (mBitmapSize.height * viewToBitmapWidthRatio);
                } else {
                    desiredHeight = heightSize;
                    desiredWidth = (int) (mBitmapSize.width * viewToBitmapHeightRatio);
                }
            }

            // Otherwise, the picture is within frame layout bounds. Desired
            // width is
            // simply picture size
            else {
                desiredWidth = mBitmapSize.width;
                desiredHeight = mBitmapSize.height;
            }

            int width = getOnMeasureSpec(widthMode, widthSize, desiredWidth);
            int height = getOnMeasureSpec(heightMode, heightSize, desiredHeight);

            mLayoutWidth = width;
            mLayoutHeight = height;

            final Rect bitmapRect = ImageViewUtil.getBitmapRectCenterInside(mBitmapSize.width,
                mBitmapSize.height,
                mLayoutWidth,
                mLayoutHeight);
            mCropOverlayView.setBitmapRect(bitmapRect);

            //jarlen
            mCropOverlayView.setBitmapSize(mBitmapSize.width, mBitmapSize.height);
            // MUST CALL THIS
            setEstimatedSize(
                EstimateSpec.getChildSizeWithMode(width, width, EstimateSpec.PRECISE),
                EstimateSpec.getChildSizeWithMode(height, height, EstimateSpec.PRECISE)
            );
            //   setEstimatedSize(Component.EstimateSpec.getChildSizeWithMode(mLayoutWidth,mLayoutWidth,Component.EstimateSpec.NOT_EXCEED),  Component.EstimateSpec.getChildSizeWithMode(540,540,Component.EstimateSpec.NOT_EXCEED));
            return true;
        } else {
            mCropOverlayView.setBitmapRect(EMPTY_RECT);
            setEstimatedSize(widthMeasureSpec, heightMeasureSpec);
            return true;
        }
    }


    protected void onLayout() {
        if (mLayoutWidth > 0 && mLayoutHeight > 0) {
            // Gets original parameters, and creates the new parameters
            final ComponentContainer.LayoutConfig origparams = this.getLayoutConfig();
            origparams.width = mLayoutWidth;
            origparams.height = mLayoutHeight;
            setLayoutConfig(origparams);
        }
    }

    // Public Methods //////////////////////////////////////////////////////////

    /**
     * Returns the integer of the imageResource
     * the image resource id
     *
     * @return int
     */
    public int getImageResource() {
        return mImageResource;
    }

    private void initCacheRotate() {
        cacheRotate = 0;
    }

    /**
     * Sets a Bitmap as the content of the CropImageView.
     * 设置剪切资源图
     *
     * @param filedesc 图片的资源
     */

    public void setImagePath(FileDescriptor filedesc) {
        ImageSource.SourceOptions sourceOptions = new ImageSource.SourceOptions();
        sourceOptions.formatHint = "image/png";
        cacheImageSource = ImageSource.create(filedesc, sourceOptions);
        ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions();
        decodingOptions.editable = true;
        PixelMap pixelMap = cacheImageSource.createPixelmap(decodingOptions);
        initCacheRotate();
        setImageBitmap(pixelMap);
    }

    /**
     * Sets a Bitmap as the content of the CropImageView.
     * 设置剪切资源图
     *
     * @param bitmap 剪切资源图
     */
    public void setImageBitmap(PixelMap bitmap) {
        mBitmap = bitmap;
        mImageView.setPixelMap(mBitmap);
        mImageView.setScaleMode(Image.ScaleMode.INSIDE);
        if (mCropOverlayView != null) {
            mCropOverlayView.resetCropOverlayView();
            mCropOverlayView.setVisibility(VISIBLE);
        }
    }

    public CropImageView switchCropOverlayViewVisibility(boolean visibility) {
        return setCropOverlayViewVisibility(visibility ? VISIBLE : HIDE);
    }

    public CropImageView setCropOverlayViewVisibility(int visibility) {
        if (mCropOverlayView != null) {
            mCropOverlayView.setVisibility(visibility);
        }
        return this;
    }

    public CropOverlayView getCropOverlayView() {
        return mCropOverlayView;
    }

    public Image getImageView() {
        return mImageView;
    }

    /**
     * Sets a Bitmap and initializes the image rotation according to the EXIT data.
     * <p>
     * The EXIF can be retrieved by doing the following:
     * <code>ExifInterface exif = new ExifInterface(path);</code>
     *
     * @param bitmap the original bitmap to set; if null, this
     * @param exif the EXIF information about this bitmap; may be null
     */
    public void setImageBitmap(PixelMap bitmap, ImageSource exif) {
        if (bitmap == null) {
            return;
        }
        initCacheRotate();
        if (exif == null) {
            setImageBitmap(bitmap);
            return;
        }

        final Matrix matrix = new Matrix();
        final int orientation = exif.getImagePropertyInt(PropertyKey.Exif.ORIENTATION, 1);
        int rotate = -1;

        switch (orientation) {
            case 8:
                rotate = 270;
                break;
            case 3:
                rotate = 180;
                break;
            case 6:
                rotate = 90;
                break;
            default:
                break;
        }
        if (rotate == -1) {
            setImageBitmap(bitmap);
        } else {
            matrix.postRotate(rotate);
            //  PixelMap.InitializationOptions initializationOptions = new PixelMap.InitializationOptions();
            //  ohos.media.image.common.Rect rect = new ohos.media.image.common.Rect(0, 0, bitmap.getImageInfo().size.width, bitmap.getImageInfo().size.height);
            mImageView.setPixelMap(bitmap);
            mImageView.setRotation(rotate);

            setImageBitmap(mImageView.getPixelMap());

        }
    }

    /**
     * Sets a Drawable as the content of the CropImageView.
     * 设置剪切资源图
     *
     * @param resId 剪切资源图ID
     */
    public void setImageResource(int resId) {
        if (resId != 0) {
            try {
                RawFileEntry assetManager = getResourceManager().getRawFileEntry(getResourceManager().getMediaPath(resId));
                ImageSource.SourceOptions options = new ImageSource.SourceOptions();
                options.formatHint = "image/jpg";
                ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions();
                decodingOptions.editable = true;
                Resource asset = assetManager.openRawFile();
                ImageSource source = ImageSource.create(asset, options);
                Optional<PixelMap> pixelMap = Optional.ofNullable(source.createPixelmap(decodingOptions));
                PixelMap bitmap = PixelMap.create(pixelMap.get(), null);
                setImageBitmap(bitmap);
                initCacheRotate();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (NotExistException e) {
                e.printStackTrace();
            } catch (WrongTypeException e) {
                e.printStackTrace();
            }

        }
    }

    /**
     * 裁剪图片
     *
     * @return PixelMap
     */
    public PixelMap cropImage() {
        return cropImage(false);
    }

    /**
     * 裁剪图片
     *
     * @param isContinueCrop
     * @return PixelMap
     */
    public PixelMap cropImage(boolean isContinueCrop) {
        PixelMap bitmap = getCroppedImage();
        if (bitmap == null) {
            return null;
        }
        setImageBitmap(bitmap);
        initCacheRotate();
        if (!isContinueCrop) {
            setCropOverlayViewVisibility(HIDE);
        }
        return bitmap;
    }

    /**
     * Gets the cropped image based on the current crop window.
     * 获取剪切区图
     *
     * @return 剪切区域图
     */
    public PixelMap getCroppedImage() {
        if (mBitmap == null) {
            return null;
        }
        final Rect displayedImageRect = ImageViewUtil.getBitmapRectCenterInside(mBitmap, mImageView);
        // Get the scale factor between the actual Bitmap dimensions and the
        // displayed dimensions for width.
        Size mBitmapSize = mBitmap.getImageInfo().size;
        final float actualImageWidth = mBitmapSize.width;
        final float displayedImageWidth = displayedImageRect.getWidth();
        final float scaleFactorWidth = actualImageWidth / displayedImageWidth;

        // Get the scale factor between the actual Bitmap dimensions and the
        // displayed dimensions for height.
        final float actualImageHeight = mBitmapSize.height;
        final float displayedImageHeight = displayedImageRect.getHeight();
        final float scaleFactorHeight = actualImageHeight / displayedImageHeight;

        // Get crop window position relative to the displayed image.
        final float cropWindowX = Edge.LEFT.getCoordinate() - displayedImageRect.left;
        final float cropWindowY = Edge.TOP.getCoordinate() - displayedImageRect.top;
        final float cropWindowWidth = Edge.getWidth();
        final float cropWindowHeight = Edge.getHeight();

        // Scale the crop window position to the actual size of the Bitmap.
        final float actualCropX = cropWindowX * scaleFactorWidth;
        final float actualCropY = cropWindowY * scaleFactorHeight;
        final float actualCropWidth = cropWindowWidth * scaleFactorWidth;
        final float actualCropHeight = cropWindowHeight * scaleFactorHeight;

        // Crop the subset from the original Bitmap.
        return CropDrawableUtils.createBitmapSafely(mBitmap,
            (int) actualCropX,
            (int) actualCropY,
            (int) actualCropWidth,
            (int) actualCropHeight,
            1);
    }


    /**
     * Gets the crop window's position relative to the source Bitmap (not the image
     * displayed in the CropImageView).
     *
     * @return a RectF instance containing cropped area boundaries of the source Bitmap
     */
    public RectFloat getActualCropRect() {

        final Rect displayedImageRect = ImageViewUtil.getBitmapRectCenterInside(mBitmap, mImageView);

        // Get the scale factor between the actual Bitmap dimensions and the
        // displayed dimensions for width.
        Size mBitmapSize = mBitmap.getImageInfo().size;
        final float actualImageWidth = mBitmapSize.width;
        final float displayedImageWidth = displayedImageRect.getWidth();
        final float scaleFactorWidth = actualImageWidth / displayedImageWidth;

        // Get the scale factor between the actual Bitmap dimensions and the
        // displayed dimensions for height.
        final float actualImageHeight = mBitmapSize.height;
        final float displayedImageHeight = displayedImageRect.getHeight();
        final float scaleFactorHeight = actualImageHeight / displayedImageHeight;

        // Get crop window position relative to the displayed image.
        final float displayedCropLeft = Edge.LEFT.getCoordinate() - displayedImageRect.left;
        final float displayedCropTop = Edge.TOP.getCoordinate() - displayedImageRect.top;
        final float displayedCropWidth = Edge.getWidth();
        final float displayedCropHeight = Edge.getHeight();

        // Scale the crop window position to the actual size of the Bitmap.
        float actualCropLeft = displayedCropLeft * scaleFactorWidth;
        float actualCropTop = displayedCropTop * scaleFactorHeight;
        float actualCropRight = actualCropLeft + displayedCropWidth * scaleFactorWidth;
        float actualCropBottom = actualCropTop + displayedCropHeight * scaleFactorHeight;

        // Correct for floating point errors. Crop rect boundaries should not
        // exceed the source Bitmap bounds.
        actualCropLeft = Math.max(0f, actualCropLeft);
        actualCropTop = Math.max(0f, actualCropTop);
        actualCropRight = Math.min(mBitmapSize.width, actualCropRight);
        actualCropBottom = Math.min(mBitmapSize.height, actualCropBottom);

        return new RectFloat(actualCropLeft,
            actualCropTop,
            actualCropRight,
            actualCropBottom);
    }

    /**
     * 设置剪切类型
     * <p>
     * false: 自由剪切
     * true : 固定大小比例剪切
     *
     * @param fixAspectRatio
     */
    public void setFixedAspectRatio(boolean fixAspectRatio) {
        mCropOverlayView.setFixedAspectRatio(fixAspectRatio);
    }

    /**
     * Sets the guidelines for the CropOverlayView to be either on, off, or to show when
     * resizing the application.
     *
     * @param guidelines Integer that signals whether the guidelines should be on, off, or
     * only showing when resizing.
     */
    public void setGuidelines(int guidelines) {
        mCropOverlayView.setGuidelines(guidelines);
    }

    /**
     * Sets the both the X and Y values of the aspectRatio.
     * 设置固定比例剪切的比例
     * 先将setFixedAspectRatio(true)设置
     * <p>
     * 例如：cropImage.setAspectRatio(40, 30);是以40:30的宽高比例剪切
     *
     * @param aspectRatioX int that specifies the new X value of the aspect ratio
     * 宽度比例
     * @param aspectRatioY int that specifies the new Y value of the aspect ratio
     * 高度比例
     */
    public void setAspectRatio(int aspectRatioX, int aspectRatioY) {
        mAspectRatioX = aspectRatioX;
        mCropOverlayView.setAspectRatioX(mAspectRatioX);
        mAspectRatioY = aspectRatioY;
        mCropOverlayView.setAspectRatioY(mAspectRatioY);
    }

    /**
     * Rotates image by the specified number of degrees clockwise. Cycles from 0 to 360
     * degrees.
     * 顺时针度数旋转图片
     * <p>
     * 0 --- 360
     *
     * @param degrees Integer specifying the number of degrees to rotate.
     * <p>
     * 旋转度数
     */

    public void rotateImage(int degrees) {
        if (mBitmap == null) {
            return;
        }
        ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions();
        decodingOptions.rotateDegrees = degrees + cacheRotate;
        decodingOptions.editable = true;
        if (cacheImageSource == null) cacheImageSource = getResourceTable();
        if (cacheImageSource == null) {
            return;
        }
        mBitmap = cacheImageSource.createPixelmap(decodingOptions);
        setImageBitmap(mBitmap);
        upDateCacheRotate(degrees);
        mDegreesRotated += mImageView.getRotation();
        mDegreesRotated = mDegreesRotated % 360;

    }

    private void upDateCacheRotate(int degrees) {
        cacheRotate = cacheRotate == 0 ? degrees : cacheRotate + degrees;

    }

    private ImageSource getResourceTable() {
        if (mBitmap == null) return null;
        ImagePacker imagePacker = ImagePacker.create();
        ByteArrayOutputStream blob = new ByteArrayOutputStream();
        imagePacker.initializePacking(blob, null);
        boolean result = imagePacker.addImage(mBitmap);
        if (result) imagePacker.finalizePacking();
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(blob.toByteArray());
        try {
            blob.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
        ImageSource imageSource = ImageSource.create(byteArrayInputStream, null);
        try {
            byteArrayInputStream.close();
            blob.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return imageSource;
    }

    /**
     * 图片翻转
     *
     * @param type 翻转类型
     * CropImageType.REVERSE_TYPE.UP_DOWN
     * CropImageType.REVERSE_TYPE.LEFT_RIGHT
     * @author jarlen
     */
    public void reverseImage(CropImageType.REVERSE_TYPE type) {
        if (mBitmap == null) {
            return;
        }
        if (type == CropImageType.REVERSE_TYPE.UP_DOWN) {
            //上下翻转
            mImageView.setScale(1, -1);
        } else if (type == CropImageType.REVERSE_TYPE.LEFT_RIGHT) {
            //左右翻转
            mImageView.setScale(-1, 1);
        }
        mBitmap = mImageView.getPixelMap();
        setImageBitmap(mBitmap);
    }

    /**
     * 更改剪切框四角的样式
     *
     * @param bit 样式图片
     * bit = null 时，默认为白色角边线
     */
    public void setCropOverlayCornerBitmap(PixelMap bit) {
        mCropOverlayView.setCropOverlayCornerBitmap(bit);
    }


    /**
     * Determines the specs for the onMeasure function. Calculates the width or height
     * depending on the mode.
     *
     * @param measureSpecMode The mode of the measured width or height.
     * @param measureSpecSize The size of the measured width or height.
     * @param desiredSize The desired size of the measured width or height.
     * @return The final size of the width or height.
     */
    private static int getOnMeasureSpec(int measureSpecMode, int measureSpecSize, int desiredSize) {
        // Measure Width
        int spec;
        if (measureSpecMode == EstimateSpec.PRECISE) {
            // Must be this size
            spec = measureSpecSize;
        } else if (measureSpecMode == EstimateSpec.NOT_EXCEED) {
            // Can't be bigger than...; match_parent value
            spec = Math.min(desiredSize, measureSpecSize);
        } else {
            // Be whatever you want; wrap_content
            spec = desiredSize;
        }
        return spec;
    }

}
